/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#ifndef SHARED_DB_SQLSTORAGE_H_
#define SHARED_DB_SQLSTORAGE_H_

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif

#include <cstdint>
#include <map>
#include <boost/unordered_map.hpp>
#include "Shared/DB/DB.h"
#include "Shared/DBC/DBCFileLoader.h"
using Shared::DBC::FieldFormat;

namespace Shared {
namespace DB {

    ///////////////////////////////////////////////////////////////////////////
    /* Storage */

    class SQLStorageBase
    {
        template<class DerivedLoader, class StorageClass> friend class SQLStorageLoaderBase;

    public:
        char const* getTableName(void) const { return _tableName; }
        char const* entryFieldName(void) const { return _entryField; }
        FieldFormat getDstFormat(std::uint32_t idx) const { return (FieldFormat)_dstFormat[idx]; }
        const char* getDstFormat(void) const { return _dstFormat; }
        FieldFormat getSrcFormat(std::uint32_t idx) const { return (FieldFormat)_srcFormat[idx]; }
        const char* getSrcFormat(void) const { return _srcFormat; }
        std::uint32_t getMaxEntry(void) const { return _maxEntry; }
        std::uint32_t getRecordCount(void) const { return _recordCount; }

        template<typename T>
        class SQLSIterator
        {
                friend class SQLStorageBase;

            public:
                T const* getValue(void) const { return reinterpret_cast<T const*>(_pointer); }
                void operator ++(void) { _pointer += _recordSize; }
                T const* operator *(void) const { return getValue(); }
                T const* operator ->(void) const { return getValue(); }
                bool operator <(const SQLSIterator& r) const { return _pointer < r._pointer; }
                void operator =(const SQLSIterator& r) { _pointer = r._pointer; _recordSize = r._recordSize; }

            private:
                SQLSIterator(char* ptr, std::uint32_t recordSize) : _pointer(ptr), _recordSize(recordSize) {}
                
                char* _pointer;
                std::uint32_t _recordSize;
        };

        template<typename T>
        SQLSIterator<T> getDataBegin(void) const { return SQLSIterator<T>(_data, _recordSize); }

        template<typename T>
        SQLSIterator<T> getDataEnd(void) const { return SQLSIterator<T>(_data + _recordCount * _recordSize, _recordSize); }

    protected:
        SQLStorageBase(void);
        virtual ~SQLStorageBase(void) { free(); }

        void initialize(const char* tableName, const char* entry_field, const char* src_format, const char* dst_format);
        std::uint32_t getDstFieldCount(void) const { return _dstFieldCount; }
        std::uint32_t getSrcFieldCount(void) const { return _srcFieldCount; }
        std::uint32_t getRecordSize(void) const { return _recordSize; }
        
        virtual void prepareToLoad(std::uint32_t maxRecordId, std::uint32_t recordCount, std::uint32_t recordSize);
        virtual void justCreatedRecord(std::uint32_t recordId, char* record) = 0;
        virtual void free(void);

    private:
        char* createRecord(std::uint32_t recordId);

        // Information about the table
        const char* _tableName;
        const char* _entryField;
        const char* _srcFormat;
        const char* _dstFormat;

        // Information about the records
        std::uint32_t _dstFieldCount;
        std::uint32_t _srcFieldCount;
        std::uint32_t _recordCount;
        std::uint32_t _maxEntry;
        std::uint32_t _recordSize;

        // Data Storage
        char* _data;
    };


    class SQLStorage : public SQLStorageBase
    {
        template<class DerivedLoader, class StorageClass> friend class SQLStorageLoaderBase;

    public:
        SQLStorage(const char* fmt, const char* _entry_field, const char* sqlname);
        SQLStorage(const char* src_fmt, const char* dst_fmt, const char* _entry_field, const char* sqlname);
        ~SQLStorage() { free(); }

        template<class T>
        T const* lookupEntry(std::uint32_t id) const
        {
            if (id >= getMaxEntry())
                { return NULL; }
            return reinterpret_cast<T const*>(_index[id]);
        }

        void load(Shared::Database &db, bool error_at_empty = true);
        void eraseEntry(std::uint32_t id);

    protected:
        void prepareToLoad(std::uint32_t maxRecordId, std::uint32_t recordCount, std::uint32_t recordSize) override;
        void justCreatedRecord(std::uint32_t recordId, char* record) override
        {
            _index[recordId] = record;
        }
        void free() override;

    private:
        char** _index; /**< Lookup access */
    };


    class SQLHashStorage : public SQLStorageBase
    {
        template<class DerivedLoader, class StorageClass> friend class SQLStorageLoaderBase;

    public:
        SQLHashStorage(const char* fmt, const char* _entry_field, const char* sqlname);
        SQLHashStorage(const char* src_fmt, const char* dst_fmt, const char* _entry_field, const char* sqlname);
        ~SQLHashStorage() { free(); }

        template<class T>
        T const* lookupEntry(std::uint32_t id) const
        {
            RecordMap::const_iterator find = _indexMap.find(id);
            if (find != _indexMap.end())
                { return reinterpret_cast<T const*>(find->second); }
            return NULL;
        }

        void load(Shared::Database &db);
        void eraseEntry(std::uint32_t id);

    protected:
        void prepareToLoad(std::uint32_t maxRecordId, std::uint32_t recordCount, std::uint32_t recordSize) override;
        void justCreatedRecord(std::uint32_t recordId, char* record) override
        {
            _indexMap[recordId] = record;
        }

        void free() override;

    private:
        typedef boost::unordered_map< std::uint32_t /*recordId*/, char* /*record*/ > RecordMap;
        RecordMap _indexMap; /**< TODO */
    };


    class SQLMultiStorage : public SQLStorageBase
    {
        template<class DerivedLoader, class StorageClass> friend class SQLStorageLoaderBase;
        template<typename T> friend class SQLMultiSIterator;
        template<typename T> friend class SQLMSIteratorBounds;

    private:
        typedef std::multimap < std::uint32_t /*recordId*/, char* /*record*/ > RecordMultiMap;

    public:
        SQLMultiStorage(const char* fmt, const char* _entry_field, const char* sqlname);
        SQLMultiStorage(const char* src_fmt, const char* dst_fmt, const char* _entry_field, const char* sqlname);
        ~SQLMultiStorage() { free(); }

        // forward declaration
        template<typename T> class SQLMSIteratorBounds;

        template<typename T>
        class SQLMultiSIterator
        {
                friend class SQLMultiStorage;
                friend class SQLMSIteratorBounds<T>;

            public:
                T const* getValue() const { return reinterpret_cast<T const*>(_cIterator->second); }
                std::uint32_t getKey() const { return _cIterator->first; }
                void operator ++() { ++_cIterator; }
                T const* operator *() const { return getValue(); }
                T const* operator ->() const { return getValue(); }
                bool operator !=(const SQLMultiSIterator& r) const { return _cIterator != r._cIterator; }
                bool operator ==(const SQLMultiSIterator& r) const { return _cIterator == r._cIterator; }

            private:
                SQLMultiSIterator(RecordMultiMap::const_iterator _itr) : _cIterator(_itr) {}

                RecordMultiMap::const_iterator _cIterator; /**< TODO */
        };

        template<typename T>
        class SQLMSIteratorBounds
        {
                friend class SQLMultiStorage;

            public:
                const SQLMultiSIterator<T> first;
                const SQLMultiSIterator<T> second;

            private:
                SQLMSIteratorBounds(std::pair<RecordMultiMap::const_iterator, RecordMultiMap::const_iterator> pair) : first(pair.first), second(pair.second) {}
        };

        template<typename T>
        SQLMSIteratorBounds<T> getBounds(std::uint32_t key) const { return SQLMSIteratorBounds<T>(_indexMultiMap.equal_range(key)); }


        void load(Shared::Database &db);
        void eraseEntry(std::uint32_t id);

    protected:
        void prepareToLoad(std::uint32_t maxRecordId, std::uint32_t recordCount, std::uint32_t recordSize) override;
        void justCreatedRecord(std::uint32_t recordId, char* record) override
        {
            _indexMultiMap.insert(RecordMultiMap::value_type(recordId, record));
        }
        void free() override;

    private:
        RecordMultiMap _indexMultiMap;
    };


    ///////////////////////////////////////////////////////////////////////////
    /* Loader */

    template <class DerivedLoader, class StorageClass>
    class SQLStorageLoaderBase
    {
    public:
        bool load(Shared::Database &db, StorageClass& storage, bool error_at_empty = true);

        template<class S, class D>
        void convert(std::uint32_t field_pos, S src, D& dst);

        void convert(std::uint32_t field_pos, char src, bool& dst) { dst = (src != 0); }
        void convert(std::uint32_t field_pos, std::uint32_t src, bool& dst) { dst = (src != 0); }
        void convert(std::uint32_t field_pos, float src, bool& dst) 
        { dst = (src > 0.0f || src < 0.0f); }

        template<class S>
        void convert_to_str(std::uint32_t field_pos, S src, char*& dst);

        template<class D>
        void convert_from_str(std::uint32_t field_pos, char const* src, D& dst);

        void convert_str_to_str(std::uint32_t field_pos, char const* src, char*& dst);

        template<class S, class D>
        void default_fill(std::uint32_t field_pos, S src, D& dst);

        void default_fill_to_str(std::uint32_t field_pos, char const* src, char*& dst);

        template<class D>
        void convert_from_str(std::uint32_t field_pos, char* src, D& dst);

        void convert_str_to_str(std::uint32_t field_pos, char* src, char*& dst);

    private:
        template<class V>
        void storeValue(V value, StorageClass& store, char* record, std::uint32_t field_pos, std::uint32_t& offset);
        
        void storeValue(char const* value, StorageClass& store, char* record, std::uint32_t field_pos, std::uint32_t& offset);
        void storeValue(char* value, StorageClass& store, char* record, std::uint32_t field_pos, std::uint32_t& offset);
    };


    class SQLStorageLoader : public SQLStorageLoaderBase<SQLStorageLoader, SQLStorage>
    {
    };


    class SQLHashStorageLoader : public SQLStorageLoaderBase<SQLHashStorageLoader, SQLHashStorage>
    {
    };

    class SQLMultiStorageLoader : public SQLStorageLoaderBase<SQLMultiStorageLoader, SQLMultiStorage>
    {
    };

}}

#endif // SHARED_DB_SQLSTORAGE_H_
